package tum0r.main;

import tum0r.enums.HashMode;
import tum0r.utils.Hash;
import tum0r.utils.StringExtension;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;

/**
 * 工程: FileSync<br>
 * 包: tum0r.main<br>
 * 创建者: tum0r<br>
 * 创建时间: 2021/6/17 09:42<br>
 * <br>
 */
public class Main {
    public static void main(String[] args) {
        if (args == null || args.length != 3 ||
                StringExtension.isNullOrEmpty(args[0]) ||
                StringExtension.isNullOrEmpty(args[1]) ||
                StringExtension.isNullOrEmpty(args[2])) {
            System.err.println("第一个参数为源文件（夹），第二个参数为目标文件（夹），第三个参数为同步间隔时间，单位秒");
            return;
        }

        File source = new File(new File(args[0]).getAbsolutePath());
        File target = new File(new File(args[1]).getAbsolutePath());
        if (!source.exists() || !source.canRead()) {
            System.err.println("源文件不存在或没有读取权限");
            return;
        }
        if (source.isHidden()) {
            System.err.println("不支持同步隐藏文件（夹）");
            return;
        }
        long time = Long.parseLong(args[2]) * 1000;
        if (time == 0) {
            sync(source, target);
        } else {
            while (true) {
                sync(source, target);
                try {
                    Thread.sleep(time);
                } catch (InterruptedException ignored) {
                    break;
                }
            }
        }
    }

    private static void sync(File source, File target) {
        if (source.isHidden()) return;
        if (source.isDirectory()) {
            if (!target.exists()) {
                target.mkdirs();
                System.out.println("创建新文件夹 " + target.getAbsolutePath());
            }
            File[] sourceFiles = source.listFiles();
            HashSet<String> sourceFilesName = new HashSet<>();
            if (sourceFiles != null) {
                for (File file : sourceFiles) {
                    if (file.isHidden()) continue;
                    sourceFilesName.add(file.getName());
                    if (file.isFile()) {
                        sync(file, target);
                    } else if (file.isDirectory()) {
                        sync(file, new File(target.getAbsolutePath() + File.separator + file.getName()));
                    }
                }
            }
            File[] targetFiles = target.listFiles();
            if (targetFiles != null) {
                for (File file : targetFiles) {
                    if (file.isHidden()) continue;
                    String name = file.getName();
                    if (!sourceFilesName.contains(name)) {
                        delete(new File(target.getAbsolutePath() + File.separator + name));
                    }
                }
            }
        } else if (source.isFile()) {
            File dir = target.getParentFile();
            if (!dir.exists()) {
                dir.mkdirs();
                System.out.println("创建新文件夹 " + dir.getAbsolutePath());
            }
            try {
                FileChannel inChannel = FileChannel.open(source.toPath(), StandardOpenOption.READ);
                File newTarget = new File(target.getAbsolutePath() + File.separator + source.getName());
                FileChannel outChannel = FileChannel.open(newTarget.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
                if (needSync(inChannel, outChannel)) {
                    System.out.println("同步 " + source.getAbsolutePath() + " => " + newTarget.getAbsolutePath());
                    inChannel.transferTo(0, inChannel.size(), outChannel);
                }
                outChannel.close();
                inChannel.close();
            } catch (IOException ignored) {
            }
        }
    }

    private static void delete(File file) {
        if (file.exists()) {
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                if (files != null) {
                    for (File temp : files) {
                        delete(temp);
                    }
                }
            }
            System.out.println("删除 " + file.getAbsolutePath());
            file.delete();
        }
    }

    private static final int MaxSize = (Integer.MAX_VALUE - 2) / 2;

    private static String getHash(FileChannel fileChannel) {
        StringBuilder result = new StringBuilder();
        try {
            long fileSize = fileChannel.size();
            ByteBuffer byteBuffer = ByteBuffer.allocate(fileSize >= MaxSize ? MaxSize : (int) fileSize);
            while (fileChannel.read(byteBuffer) != 0) {
                result.append(Hash.encrypt(byteBuffer.array(), HashMode.SHA256));
            }
            byteBuffer = null;
            System.gc();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result.toString();
    }

    private static boolean needSync(FileChannel inChannel, FileChannel outChannel) {
        boolean result = false;
        try {
            result = inChannel.size() != outChannel.size();
            if (!result) {
                result = !getHash(inChannel).equals(getHash(outChannel));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}
